
#include <iostream>
#include <fstream>
#include <sstream>
#include <cassert>
#include <algorithm>

#if defined(WIN32)
    #include <winsock2.h>
    #if defined(ENABLE_OPENSSL)
    /// Avoid link errors caused by the openssl static library compiled by the older VS compiler
    // link errors about __iob_func
    extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; }
    // Avoid link errors about printf/scanf
    #pragma comment(lib,"legacy_stdio_definitions.lib")
    #endif // #if defined(ENABLE_OPENSSL)
#else
    #include <string.h>
    #include <netinet/in.h>
#endif

#include "eXosip2/eXosip.h"

#include "sipua.h"
#include "sipreg.h"
#include "sipcall.h"
#include "zklog/zklog_wrapper.h"

using namespace std;

//SipUserAgent////////////////////////////////////////////////////////////////////////
SipUserAgent::SipUserAgent(ConfigParam& param) : SipCore(param), mReg(NULL), mCall(NULL)
{
}

SipUserAgent::~SipUserAgent()
{
}

bool SipUserAgent::init()
{
    if (SipCore::init())
    {
        mReg = new SipReg(mSipCtx, mParam);
        mCall = new SipCall(mSipCtx, mParam);
        return true;
    }

    return false;
}

void SipUserAgent::uninit()
{
    if (mCall) { terminate_call("sipclient uninit."); delete mCall; mCall = NULL; }
    if (mReg) { doUnRegister(); delete mReg; mReg = NULL; }

    SipCore::uninit();
}

bool SipUserAgent::doRegister()
{
    if (mReg->success())
    {
        ZKLOG_WARNING_FMT("not support multiple register for now!");
        return false;
    }
    else if (mReg->inReg())
    {
        ZKLOG_WARNING_FMT("a previous register is in process...!");
        return false;
    }

    return mReg->sendRegister(0, mParam.mRegExpires);
}

void SipUserAgent::doUnRegister()
{
    mReg->sendUnRegister();
}

bool SipUserAgent::send_call(const std::string& targetid)
{
    if (mReg && mReg->success())
    {
        ifstream ifs("sdp.txt");
        if (!ifs)
        {
            ZKLOG_WARNING_FMT("not exist sdp.txt!");
            return false;
        }

        stringstream ss;
        ss << ifs.rdbuf();
        string sdp(ss.str());
        ifs.close();

        return mCall->sendOffer(targetid, sdp);
    }
    else
    {
        ZKLOG_WARNING_FMT("not register!");
        return false;
    }
}

void SipUserAgent::terminate_call(const std::string& reason /*= ""*/)
{
    if (mReg && mReg->success())
    {
        mCall->terminate(reason);
    }
}

void SipUserAgent::send_info(const std::string& msg, void* appdata)
{
    if (mCall->isConnected())
    {
        mCall->sendCallRequest("INFO", "text/plain", msg, appdata);
    }
    else
    {
        ZKLOG_WARNING_FMT("call does not exist, only support INFO in-dialog!");
    }
}

bool SipUserAgent::sip_send_message_request(const std::string& method, const std::string& contenttype, const std::string& msg)
{
    if (!mReg->success())
    {
        ZKLOG_WARNING_FMT("not register!");
        return false;
    }

    int ret = OSIP_SUCCESS;
    osip_message_t *req = NULL;
    ret = eXosip_message_build_request(mSipCtx, &req, method.c_str(), mParam.get_proxy().c_str(), mParam.get_from().c_str(), mParam.get_route().c_str());
    if (OSIP_SUCCESS != ret)
    {
        ZKLOG_ERROR_FMT("failed to build request");
        return false;
    }
    ret = osip_message_set_content_type(req, contenttype.c_str());
    ret = osip_message_set_body(req, msg.c_str(), msg.size());

    ret = eXosip_message_send_request(mSipCtx, req);
    if (ret > 0)
    {
        ZKLOG_INFO_FMT("send request, tid={0}, method={1}, content type={2}, content length={3}, content body=\n{4}",
            ret, method, contenttype, msg.size(), msg);
        return false;
    }
    else
    {
        ZKLOG_ERROR_FMT("failed to send request!");
        return true;
    }
}

bool SipUserAgent::sip_send_message_answer(int tid, int statuscode, const std::string& contenttype, const std::string& msg)
{
    if (!mReg->success())
    {
        ZKLOG_WARNING_FMT("not register!");
        return false;
    }

    int ret = OSIP_SUCCESS;
    osip_message_t *answer = NULL;
    if (msg.size() > 0)
    {
        ret = eXosip_message_build_answer(mSipCtx, tid, statuscode, &answer);
        if (OSIP_SUCCESS != ret)
        {
            ZKLOG_ERROR_FMT("failed to build answer to tid={}", tid);
            return false;
        }
        ret = osip_message_set_content_type(answer, contenttype.c_str());
        ret = osip_message_set_body(answer, msg.c_str(), msg.size());
    }

    ret = eXosip_message_send_answer(mSipCtx, tid, statuscode, answer);
    if (OSIP_SUCCESS == ret)
    {
        ZKLOG_INFO_FMT("send answer to tid={}, statuscode={}, content type={}, content length={}, content body=\n{}",
            tid, statuscode, contenttype, msg.size(), msg);
        return true;
    }
    else
    {
        ZKLOG_ERROR_FMT("failed to send answer to tid={}", tid);
        return false;
    }
}

void SipUserAgent::proc_register_response(const eXosip_event* const osipEvt)
{
    string nataddr;
    int natport = 0;
    mReg->onRegister(osipEvt, nataddr, natport);
    if (!nataddr.empty())
    {
        mCall->updateNatInfo(nataddr, natport);
    }
}

void SipUserAgent::proc_call_request(const eXosip_event* const osipEvt)
{
    mCall->onOffer(osipEvt);
}

void SipUserAgent::proc_call_response(const eXosip_event* const osipEvt)
{
    if (osipEvt->type == EXOSIP_CALL_PROCEEDING)
    {
        mCall->on100(osipEvt);
    }
    else if (osipEvt->type == EXOSIP_CALL_RINGING)
    {
        mCall->on180(osipEvt);
    }
    else if (osipEvt->type == EXOSIP_CALL_ANSWERED)
    {
        mCall->onAnswer(osipEvt);
    }
    else if (osipEvt->type == EXOSIP_CALL_REQUESTFAILURE)
    {
        if (osipEvt->response)
        {
            if (osipEvt->response->status_code == 407 || osipEvt->response->status_code == 401)
            {
                //
            }
            else
            {
                mCall->onFail(osipEvt);
            }
        }
        else
        {
            mCall->onFail(osipEvt);
        }
    }
    else
    {
        mCall->onFail(osipEvt);
    }
}

void SipUserAgent::proc_call_ack(const eXosip_event* const osipEvt)
{
    mCall->onAck(osipEvt);
}

void SipUserAgent::proc_call_cancel(const eXosip_event * const osipEvt)
{
    mCall->onCancel(osipEvt);
}

void SipUserAgent::proc_call_message_request(const eXosip_event* const osipEvt)
{
    if (!MSG_IS_BYE(osipEvt->request))
    {//in-dialog request
        char* body;
        int pos = 0;
        while (!osip_list_eol(&osipEvt->request->bodies, pos)) {
            osip_body_t* oldbody = (osip_body_t *)osip_list_get(&osipEvt->request->bodies, pos);
            body = oldbody->body;
            pos++;
            if (body)
                break;
            body = NULL;
        }

        common::ostring contenttype;
        osip_content_type_to_str(osip_message_get_content_type(osipEvt->request), &contenttype);

        if (NULL == body)
        {
            ZKLOG_INFO_FMT("received in-dialog message, method={}, content type={}",
                osip_message_get_method(osipEvt->request), contenttype);
        }
        else
        {
            ZKLOG_INFO_FMT("received in-dialog message, method={}, content type={}, content body=\n{}",
                osip_message_get_method(osipEvt->request), contenttype, body);
        }
    }
    else
    {//received BYE
    }
}

void SipUserAgent::proc_call_message_response(const eXosip_event* const osipEvt)
{
    int appdata = (intptr_t)osipEvt->request->application_data;

    if (osipEvt->type == EXOSIP_CALL_MESSAGE_ANSWERED)
    {
        if (!strcmp(osipEvt->request->sip_method, "BYE"))
        {//received response 200OK for BYE
            ZKLOG_INFO_FMT("<== [{} {}] BYE response. did={}, cid={}, tid={}",
                osipEvt->response->status_code, osip_message_get_reason(osipEvt->response->status_code),
                osipEvt->did, osipEvt->cid, osipEvt->tid);
            mCall->transitionState(SipCall::CALL_STATUS_COMPLETE);
        }
        else if (!strcmp(osipEvt->request->sip_method, "CANCEL"))
        {//received response 200OK for CANCEL
            ZKLOG_INFO_FMT("<== [{} {}] CANCEL response. did={}, cid={}, tid={}",
                osipEvt->response->status_code, osip_message_get_reason(osipEvt->response->status_code),
                osipEvt->did, osipEvt->cid, osipEvt->tid);
            mCall->transitionState(SipCall::CALL_STATUS_COMPLETE);
        }
        else
        {
            ZKLOG_INFO_FMT("received in-dialog message response 200OK from={}", appdata);
        }
    }
    else
    {
        ZKLOG_INFO_FMT("received in-dialog message failure from={}", appdata);
    }
}

void SipUserAgent::proc_call_close(const eXosip_event * const osipEvt)
{
    mCall->onBye(osipEvt);
}

void SipUserAgent::proc_call_release(const eXosip_event * const osipEvt)
{
    mCall->releaseCall(osipEvt);
}

void SipUserAgent::proc_new_message(const eXosip_event* const osipEvt)
{
    if(MSG_IS_OPTIONS(osipEvt->request))
    {
        ZKLOG_INFO_FMT("RECEIVED OPTIONS");
        //osip_message_t* answer;
        //eXosip_options_build_answer(mSipCtx, osipEvt->tid, 200, &answer);
        //eXosip_options_send_answer(mSipCtx, osipEvt->tid, 200, answer);
    }
    else if (MSG_IS_MESSAGE(osipEvt->request))
    {
        ZKLOG_INFO_FMT("RECEIVED MESSAGE");
    }
    else if (MSG_IS_INFO(osipEvt->request))
    {
        ZKLOG_INFO_FMT("RECEIVED INFO");
    }
}

void SipUserAgent::proc_message_response(const eXosip_event * const osipEvt)
{
}

